const std = @import("std");
const rl = @import("raylib");
const utils = @import("../utils.zig");
const setting = @import("../setting.zig");
const math = std.math;
const Cell = @import("../cell.zig");

const BaseWalkSpeed: f32 = 4.0;
const MaxLimitTimes: f32 = 1.5;
const TimesQuickWalk2Walk: f32 = 1.3;
const TimesSlowRun2Walk: f32 = 1.6;
const TimesRun2Walk: f32 = 2.4;
const TimesQuickRun2Walk: f32 = 3.0;

const Move = @This();

cell: *Cell,

speed: f32 = -1.0,
cur_direction: f32 = -1.0,
cur_to_walk_time: f32 = -1.0,
cur_walk_time: f32 = -1.0,

// 基因属性
walk_speed: f32 = -1.0,
quick_walk_speed: f32 = -1.0,
slow_run_speed: f32 = -1.0,
run_speed: f32 = -1.0,
quick_run_speed: f32 = -1.0,

pub fn init(cell: *Cell) @This() {
    const tmp_walk_speed: f32 = @floatFromInt(cell.rand.intRangeAtMost(u32, @intFromFloat(BaseWalkSpeed), @intFromFloat(BaseWalkSpeed * MaxLimitTimes)));
    const _quick_walk_speed: f32 = tmp_walk_speed * TimesQuickWalk2Walk;
    const tmp_quick_walk_speed: f32 = @floatFromInt(cell.rand.intRangeAtMost(u32, @intFromFloat(_quick_walk_speed), @intFromFloat(_quick_walk_speed * MaxLimitTimes)));
    const _slow_run_speed: f32 = tmp_quick_walk_speed + tmp_walk_speed * (TimesSlowRun2Walk - TimesQuickWalk2Walk);
    const tmp_slow_run_speed: f32 = @floatFromInt(cell.rand.intRangeAtMost(u32, @intFromFloat(_slow_run_speed), @intFromFloat(_slow_run_speed * MaxLimitTimes)));
    const _run_speed: f32 = tmp_slow_run_speed + tmp_walk_speed * (TimesRun2Walk - TimesSlowRun2Walk);
    const tmp_run_speed: f32 = @floatFromInt(cell.rand.intRangeAtMost(u32, @intFromFloat(_run_speed), @intFromFloat(_run_speed * MaxLimitTimes)));
    const _quick_run_speed: f32 = tmp_run_speed + tmp_walk_speed * (TimesQuickRun2Walk - TimesRun2Walk);
    const tmp_quick_run_speed: f32 = @floatFromInt(cell.rand.intRangeAtMost(u32, @intFromFloat(_quick_run_speed), @intFromFloat(_quick_run_speed * MaxLimitTimes)));

    return .{
        .cell = cell,
        .walk_speed = tmp_walk_speed,
        .quick_walk_speed = tmp_quick_walk_speed,
        .slow_run_speed = tmp_slow_run_speed,
        .run_speed = tmp_run_speed,
        .quick_run_speed = tmp_quick_run_speed,
    };
}

pub fn deinit(self: *@This()) void {
    _ = self;
}

pub fn walk4Nothing(self: *@This()) void {
    const frame_time: f32 = rl.getFrameTime();
    if (self.cur_direction < 0 or self.cur_to_walk_time <= self.cur_walk_time) {
        self.cur_direction = @floatFromInt(self.cell.rand.intRangeAtMost(u32, 0, 360));
        self.cur_to_walk_time = @floatFromInt(self.cell.rand.intRangeAtMost(u32, self.cell.short_willpower_time, Cell.MaxShortWillpowerTime));
        self.cur_walk_time = 0.0;
    }

    self.walkToAngle(self.cur_direction);
    self.cur_walk_time += frame_time;

    self.cell.sys_energy.energy_drained += self.cell.sys_energy.idle_energy_drain * 3.0 * frame_time;
}

pub fn walkToPoint(self: *@This(), x: f32, y: f32) void {
    self.toPoint(x, y, self.doWalk());
}

pub fn walkToAngle(self: *@This(), degree: f32) void {
    self.toAngle(degree, self.doWalk());
}

pub fn runToPoint(self: *@This(), x: f32, y: f32) void {
    self.toPoint(x, y, self.doRun());
}

pub fn runToAngle(self: *@This(), degree: f32) void {
    self.toAngle(degree, self.doRun());
}

fn doWalk(self: *@This()) f32 {
    if (self.speed < 0.0) {
        self.speed = utils.FetchPlusValue(self.walk_speed);
    }
    return self.speed * rl.getFrameTime();
}

fn doRun(self: *@This()) f32 {
    const times = rl.getFrameTime();

    const _normal_speed = utils.FetchPlusValue(self.walk_speed);
    const _acceleration = utils.FetchPlusValue(self.acceleration);

    var distance: f32 = 0.0;
    if (self.speed < 0.0) {
        self.speed = _normal_speed + _acceleration * times;
        distance = _normal_speed * times + _acceleration * times * times;
    } else {
        self.speed = self.speed + _acceleration * times;
        distance = self.speed * times + _acceleration * times * times;
    }

    if (self.max_speed > 0.0 and self.speed > self.max_speed) {
        self.speed = self.max_speed;
    }

    return distance;
}

fn toPoint(self: *@This(), x: f32, y: f32, distance: f32) void {
    const vector_distance = math.pow(f32, (x - self.x) * (x - self.x) + (y - self.y) * (y - self.y), 0.5);

    if (distance >= vector_distance) {
        self.cell.x = x;
        self.cell.y = y;
    } else {
        self.cell.x = self.cell.x + distance * (x - self.cell.x) * vector_distance;
        self.cell.y = self.cell.y + distance * (y - self.cell.y) * vector_distance;
    }
}

/// 朝下为零度，逆时针旋转为角度增加方向
fn toAngle(self: *@This(), degree: f32, distance: f32) void {
    const radius: f32 = math.rad_per_deg * degree;

    self.cell.x = self.cell.x + distance * math.sin(radius);
    self.cell.y = self.cell.y + distance * math.cos(radius);

    const world_width: f32 = @floatFromInt(setting.world_width - 1);
    if (self.cell.x > world_width) {
        self.cell.x = self.cell.x - world_width;
    }
    if (self.cell.x < 0.0) {
        self.cell.x = world_width - self.cell.x;
    }

    const world_height: f32 = @floatFromInt(setting.world_height - 1);
    if (self.cell.y > world_height) {
        self.cell.y = self.cell.y - world_height;
    }
    if (self.cell.y < 0.0) {
        self.cell.y = world_height - self.cell.y;
    }
}
